package app

import (
	"dtf/service_control_center/functions"
	"encoding/json"
	"errors"
	"fmt"
	mqtt "github.com/eclipse/paho.mqtt.golang"
	"github.com/gogf/gf/encoding/gjson"
	"github.com/gogf/gf/frame/g"
	"github.com/gogf/gf/net/ghttp"
	"github.com/gogf/gf/os/gfile"
	"strings"
	"time"
)

/*
	Agent表示在受控端运行的任务执行器服务，可以用http调用，也可以用mqtt调用
*/

// TAgentConfig Agent 设置项
type TAgentConfig struct {
	CCHost              string       `json:"cc_host"`            //控制中心IP
	CCPort              int          `json:"cc_port"`            //控制中心端口
	Uid                 string       `json:"uid"`                //唯一识别号，用于订阅Topic消息主题
	Name                string       `json:"name"`               //名称
	Description         string       `json:"description"`        //描述
	CryptEnabled        bool         `json:"crypt_enabled"`      //是否启用报文加密
	PublicPemFile       string       `json:"public_pem_file"`    //通讯加密公钥文件路径
	NotifyEnabled       bool         `json:"notify_enabled"`     //是否启用通知功能，主要作用是提供外部程序通过HTTP提交主控端发送MQTT消息的能力
	HttpEnabled         bool         `json:"http_enabled"`       //是否启用了http服务
	HttpHost            string       `json:"http_host"`          //本Agent所在服务器的IP地址
	HttpPort            int          `json:"http_port"`          //本Agent监听端口
	HttpMessageEndpoint string       `json:"http_endpoint"`      //HTTP报文提交URL入口
	MqttEnabled         bool         `json:"mqtt_enabled"`       //是否启用了mqtt服务
	MqttHost            string       `json:"mqtt_host"`          //MQTT服务器地址
	MqttPort            int          `json:"mqtt_port"`          //MQTT服务器端口
	MqttUser            string       `json:"mqtt_user"`          //MQTT用户名
	MqttPassword        string       `json:"mqtt_password"`      //MQTT密码
	MqttMessageTopic    string       `json:"mqtt_message_topic"` //MQTT报文接收主题
	MqttResultTopic     string       `json:"mqtt_result_topic"`  //MQTT回报发送主题
	MqttNotifyTopic     string       `json:"mqtt_notify_topic"`  //MQTT通知消息发送主题
	SupportTask         []TAgentTask `json:"support_task"`       //支持的任务列表
	MaxTasks            int          `json:"max_tasks"`          //最大执行Tasks
}

// SaveToJsonFile  从JSON文件里读取设置
func (cfg *TAgentConfig) SaveToJsonFile(AFilename string) error {
	mJson := gjson.New(cfg)
	er := gfile.PutContents(AFilename, mJson.Export())
	return er
}

// LoadFromJsonFile  将设置保存到JSON文件中
func (cfg *TAgentConfig) LoadFromJsonFile(AFilename string) error {
	if gfile.Exists(AFilename) == false {
		return errors.New("file not exists")
	} else {
		mStr := gfile.GetContents(AFilename)
		er := json.Unmarshal([]byte(mStr), cfg)
		return er
	}
}

// Export Config输出成字符串
func (cfg *TAgentConfig) Export() string {
	mJson := gjson.New(cfg)
	return mJson.Export()
}

// ExportSupportTask 输出SupportTask的JSON字符串
func (cfg *TAgentConfig) ExportSupportTask() string {
	mJson := gjson.New(cfg.SupportTask)
	return mJson.Export()
}

// ClearSupportTask 清除支持的任务列表
func (cfg *TAgentConfig) ClearSupportTask() {
	cfg.SupportTask = cfg.SupportTask[:0]
}

// AddSupportTask 增加支持的任务列表
func (cfg *TAgentConfig) AddSupportTask(ATask TAgentTask) {
	cfg.SupportTask = append(cfg.SupportTask, ATask)
}

// TAgentTask 任务，记录当前正在执行的任务
type TAgentTask struct {
	Command    string    `json:"command"`    //命令号，与TAgentMessage中命令常量相同
	Name       string    `json:"name"`       //任务名称，用于注释标识
	ShellCmd   string    `json:"shell_cmd"`  //执行的Shell命令字符串
	Pid        int       `json:"pid"`        //执行进程的PID
	StartTime  time.Time `json:"start_time"` //开始执行时间
	Protocol   string    `json:"protocol"`   //调用通道（http调用或MQTT调用）
	ExecParams g.Map     `json:"execParams"` //传入的参数
	ShellArgs  []string  `json:"args"`       //运行参数
	ExecMode   int       `json:"execMode"`   //运行模式，0=doShell，1=StartProcess
}

// TAgentComponent Agent执行器对象，用于创建Agent实例
type TAgentComponent struct {
	Config     TAgentConfig   //设置项
	HttpServer *ghttp.Server  //http服务
	MqttClient TMQTTComponent //MQTT客户端
	Tasks      []TAgentTask   //正在执行进程列表
}

// Export TASK输出成Json字符串
func (task *TAgentTask) Export() string {
	mJson := gjson.New(task)
	return mJson.Export()
}

// RunTask 运行Task
func (agent *TAgentComponent) RunTask(ATask TAgentTask) {
	//异步启动新进程
	_, mPid, mEr := functions.DoShell(ATask.ShellCmd)
	var mTask TAgentTask
	if mEr == nil {
		mTask.Pid = mPid
		mTask.ShellCmd = ATask.ShellCmd
		mTask.Name = ATask.Name
		mTask.ExecParams = ATask.ExecParams
		mTask.Protocol = ATask.Protocol
		mTask.StartTime = time.Now()
		agent.Tasks = append(agent.Tasks, mTask)
	}
}

// StartProcess 异步运行Task，呼起新进程
func (agent *TAgentComponent) StartProcess(ATask TAgentTask) {
	mPid, mEr := functions.SystemCall(ATask.ShellCmd, ATask.ShellArgs)
	var mTask TAgentTask
	if mEr == nil {
		mTask.Pid = mPid
		mTask.ShellCmd = ATask.ShellCmd
		mTask.Name = ATask.Name
		mTask.ExecParams = ATask.ExecParams
		mTask.Protocol = ATask.Protocol
		mTask.StartTime = time.Now()
		agent.Tasks = append(agent.Tasks, mTask)
	}
}

// FindTaskByCommand 根据Command查找SupportTask中的指令
func (agent *TAgentComponent) FindTaskByCommand(ACommand string) (TAgentTask, error) {
	var mR TAgentTask
	var mErr error
	var mFound bool = false
	for _, item := range agent.Config.SupportTask {
		if item.Command == ACommand {
			mR = item
			mFound = true
			break
		}
	}
	if mFound == true {
		return mR, mErr
	} else {
		return mR, errors.New("task not found")
	}
}

// Init 初始化Agent组件，初始化之前必须先填充Config
func (agent *TAgentComponent) InitAndRun() error {
	var mEr error
	//检查变量通讯密钥
	if agent.Config.CryptEnabled == true {
		if strings.TrimSpace(agent.Config.PublicPemFile) == "" {
			mEr = errors.New("public pem file was empty")
		} else {
			if gfile.Exists(agent.Config.PublicPemFile) == false {
				mEr = errors.New("public pem file not found")
			}
		}
	}
	//检查http相关变量
	if agent.Config.HttpEnabled == true {
		if (strings.TrimSpace(agent.Config.HttpHost) == "") || (agent.Config.HttpPort == 0) || (strings.TrimSpace(agent.Config.HttpMessageEndpoint) == "") {
			mEr = errors.New("http host/port/endpoint was empty")
		}
	}
	//检查MQTT相关变量
	if agent.Config.MqttEnabled == true {
		if (strings.TrimSpace(agent.Config.MqttHost) == "") || (agent.Config.MqttPort == 0) || (strings.TrimSpace(agent.Config.MqttMessageTopic) == "") || (strings.TrimSpace(agent.Config.MqttResultTopic) == "") {
			mEr = errors.New("mqtt host/port/messageTopic/resultTopic was empty")
		}
	}
	//检查Notify相关设置
	if (agent.Config.NotifyEnabled == true) && (agent.Config.MqttEnabled == true) {
		if strings.TrimSpace(agent.Config.MqttNotifyTopic) == "" {
			mEr = errors.New("mqtt notify topic was empty")
		}
	}
	fmt.Println("1", mEr)
	if mEr == nil {
		//创建Mqtt Client
		if agent.Config.MqttEnabled == true {
			agent.MqttClient = TMQTTComponent{}
			//填充初始化内容
			agent.MqttClient.Host = agent.Config.MqttHost
			agent.MqttClient.Port = agent.Config.MqttPort
			agent.MqttClient.User = agent.Config.MqttUser
			agent.MqttClient.Password = agent.Config.MqttPassword
			agent.MqttClient.OnConnect = agent.MqttClientConnectHandler
			agent.MqttClient.OnMessage = agent.MqttClientMessageHandler
			agent.MqttClient.OnConnectionLost = agent.MqttClientConnectLostHandler
			//启动并连接MQTT服务器
			mEr = agent.MqttClient.InitAndConnect()
			fmt.Println("启动并连接MQTT服务器", mEr)
			if mEr == nil {
				//订阅消息主题
				agent.MqttClient.Sub(agent.Config.MqttMessageTopic)
				//订阅私有专用主题（UID号作为主题）
				if strings.TrimSpace(agent.Config.Uid) != "" {
					agent.MqttClient.Sub(agent.Config.Uid)
				}
			} else {
				fmt.Println("MQTT Connect Err", mEr)
			}
		}
		//开始初始化，创建http server
		if agent.Config.HttpEnabled == true {
			agent.HttpServer = g.Server()
			//绑定Host地址
			agent.HttpServer.SetAddr(agent.Config.HttpHost)
			//绑定Port端口
			agent.HttpServer.SetPort(agent.Config.HttpPort)
			//绑定报文入口路由
			agent.HttpServer.BindHandler(strings.TrimSpace(agent.Config.HttpMessageEndpoint), agent.HTTPMessageHandler)
			//绑定外部发送MQTT消息入口
			if agent.Config.NotifyEnabled == true {
				//绑定路由
				agent.HttpServer.BindHandler("/notify", agent.HTTPNotifyHandler)
			}
			agent.HttpServer.Run()
		}
	}
	return mEr
}

// HTTPNotifyHandler HTTP服务的外部通知处理
func (agent *TAgentComponent) HTTPNotifyHandler(r *ghttp.Request) {
	fmt.Printf("Notify request OK")
}

// SendNotifyByMqtt 发送MQTT通知消息
func (agent *TAgentComponent) SendNotifyByMqtt(AMessage TNotifyMessage) {
	if agent.MqttClient.Client.IsConnected() == true {
		mJson := gjson.New(AMessage.Payload)
		mStr := mJson.Export()
		if agent.Config.CryptEnabled == false {
			//如果未启用报文加密
			_ = agent.MqttClient.Publish(AMessage.Topic, mStr)
		} else {
			//启用了报文加密
			mCrypt := functions.RSA_Encrypt([]byte(mStr), agent.Config.PublicPemFile)
			mStr = string(mCrypt)
			_ = agent.MqttClient.Publish(AMessage.Topic, mStr)
		}
	}
}

// HTTPMessageHandler HTTP服务的报文提交处理
func (agent *TAgentComponent) HTTPMessageHandler(r *ghttp.Request) {
	fmt.Println("Http Message OK")
	var mMessage TAgentMessage
	//取data字段
	mData := r.GetString("data")
	if strings.TrimSpace(mData) == "" {
		//字段为空，返回错误
		_ = r.Response.WriteJsonExit(g.Map{
			"code": functions.ParamsError,
		})
	}
	if agent.Config.CryptEnabled == true {
		//启用了报文加密，先解密收到的报文Data
		mEr := mMessage.Decrypt(mData, agent.Config.PublicPemFile)
		if mEr != nil {
			_ = r.Response.WriteJsonExit(g.Map{
				"code": functions.CryptError,
			})
		}
	} else {
		//未启用报文加密，直接反解mData
		mEr := json.Unmarshal([]byte(mData), &mMessage)
		if mEr != nil {
			_ = r.Response.WriteJsonExit(g.Map{
				"code": functions.UnmarshalError,
			})
		}
	}
	//TODO: 打印mMessage
	fmt.Println(mMessage)
	//根据mMessage的Cmd类型执行Task，先处理内置功能
	switch mMessage.Command {
	case Cmd_QueryAgentConfig:
		mData := agent.Config.Export()
		if agent.Config.CryptEnabled == true {
			mCrypt := functions.RSA_Encrypt([]byte(mData), agent.Config.PublicPemFile)
			mData = string(mCrypt)
		}
		//查询当前设置
		_ = r.Response.WriteJsonExit(g.Map{
			"code": functions.NoError,
			"data": mData,
		})
	case Cmd_QuerySupporTasks:
		mData := agent.Config.ExportSupportTask()
		if agent.Config.CryptEnabled == true {
			mCrypt := functions.RSA_Encrypt([]byte(mData), agent.Config.PublicPemFile)
			mData = string(mCrypt)
		}
		//查询支持任务
		_ = r.Response.WriteJsonExit(g.Map{
			"code": functions.NoError,
			"data": mData,
		})
	default:
		//查询指定的TASK command并执行之
		mTask, er := agent.FindTaskByCommand(mMessage.Command)
		//发送执行开始任务通知
		var mBeforeNotify TNotifyMessage
		mBeforeNotify.Topic = agent.Config.MqttNotifyTopic
		mBeforeNotify.Sender = "Agent_http" + agent.Config.Name + "_" + agent.Config.Uid
		mBeforeNotify.SendTime = time.Now()
		mBeforeNotify.Payload = g.Map{
			"task": mTask.Export(),
		}
		mBeforeNotify.EventName = "before_task_run"
		agent.SendNotifyByMqtt(mBeforeNotify)
		if er == nil {
			switch mTask.ExecMode {
			case 0:
				agent.RunTask(mTask)
			case 1:
				agent.StartProcess(mTask)
			}
		}
		//发送执行完毕消息
		var mAfterNotify TNotifyMessage
		mAfterNotify.Topic = agent.Config.MqttNotifyTopic
		mAfterNotify.Sender = "Agent_http" + agent.Config.Name + "_" + agent.Config.Uid
		mAfterNotify.SendTime = time.Now()
		mAfterNotify.Payload = g.Map{
			"task": mTask.Export(),
		}
		mAfterNotify.EventName = "after_task_run"
		agent.SendNotifyByMqtt(mAfterNotify)
	}
}

// MqttClientConnectHandler MQTT服务的连接事件处理
func (agent *TAgentComponent) MqttClientConnectHandler(Client mqtt.Client) {
	fmt.Println("MQTT Connected")
}

// MqttClientMessageHandler Mqtt服务的消息事件处理
func (agent *TAgentComponent) MqttClientMessageHandler(Client mqtt.Client, Message mqtt.Message) {
	fmt.Printf("Received message\nTopic:%s\nMessage:%s\n", Message.Topic(), Message.Payload())
	if agent.Config.CryptEnabled == true {
		//启用了报文加密，先解密收到的报文Payload
	}
}

// MqttClientConnectLostHandler MQTT服务的断开连接事件处理
func (agent *TAgentComponent) MqttClientConnectLostHandler(Client mqtt.Client, er error) {
	fmt.Println("MQTT Connection lost Error=" + er.Error())
}
